简单看一下 微软新出的内核页表隔离补丁
文 | hzqst
看雪论坛
相比以往的内核多了一套用户层专用的页表,里面没有内核内存的映射(除了几个r3进r0的入口)
KiEnableKvaShadowing由KiInitializeBootStructures和KxInitializeProcessorState调用,应该只初始化一次
signed __int64 __fastcall KiEnableKvaShadowing(__int64 a1, __int64 a2)
{
__int64 v3; // rdx@3
__int64 v4; // rcx@3
__int64 v5; // r8@3
char v6; // al@4
signed __int64 v7; // r9@8
signed __int64 v8; // r10@8
signed __int64 v9; // rdx@8
signed __int64 v10; // rcx@10
unsigned __int64 v15; // rax@14
__int64 v16; // rdx@15
signed __int64 result; // rax@21
_RBX = a1;
if ( !(unsigned __int8)KiIsKvaShadowDisabled() )
{
if ( (unsigned __int8)KiIsKvaLeakSimulated() )
{
v6 = 1;
KiKvaLeakageSimulate = 1;
}
else
{
v6 = KiKvaLeakageSimulate;
}
if ( !KiKvaLeakage && !v6 )
return 1i64;
*(_QWORD *)(v4 + 28288) = __readcr3();
v7 = v3 + 4132;
v8 = 7i64;
*(_QWORD *)(v3 + 4216) = *(_QWORD *)(v3 + 4100);
*(_QWORD *)(v3 + 4100) = v3 + 16896;
v9 = v3 + 17376;
do
{
if ( *(_QWORD *)v7 )
{
v10 = *(_QWORD *)v7 - 32i64;
*(_QWORD *)v9 = _RBX - 384;
*(_QWORD *)(v9 + 8) = v10;
*(_QWORD *)v10 = v9;
*(_QWORD *)v7 = v9;
}
v9 += 512i64;
v7 += 8i64;
--v8;
}
while ( v8 );
if ( *(_DWORD *)(_RBX + 36) )
{
result = KiShadowProcessorAllocation(_RBX, v5);
if ( !(_DWORD)result )
return result;
*(_DWORD *)(_RBX + 28312) |= 2u;
goto LABEL_23;
}
KiInitializeIdt(v5, 1);
*(_BYTE *)(*(_QWORD *)(*MK_FP(__GS__, 392i64) + 184i64) + 703i64) = 1;
byte_1403BFBFF = 1;
KiSetAddressPolicy(_CF, _ZF, _SF, _OF, 1);
if ( *(_QWORD *)(_RBX + 25192) & 0x40000000000i64 )
{
v15 = __readcr4() & 0xFFFFFFFFFFFFFF7Fui64;
_bittestandset(&v15, 0x11u);
__writecr4(v15);
__writecr3(__readcr3() | 2);
KiFlushPcid = 1;
}
HvlRescindEnlightenments(0x40000000000i64, -129i64);
KiKvaShadow = 1;
if ( KiFlushPcid )
{
if ( *(_BYTE *)(_RBX + 1597) != 1 )
{
LABEL_20:
KiKvaShadowMode = 1;
LABEL_23:
if ( KiFlushPcid )
__asm { lock bts qword ptr [rbx+6E80h], 3Fh }
return 1i64;
}
}
else if ( *(_BYTE *)(_RBX + 1597) != 1 )
{
KiKvaShadowMode = 2;
return 1i64;
}
__writecr4(v16 & __readcr4());
goto LABEL_20;
}
KiIsKvaShadowConfigDisabled = 1;
return 1i64;
}
关键代码
signed __int64 __fastcall KiShadowProcessorAllocation(__int64 a1, __int64 a2)
{
__int64 v2; // rsi@1
__int64 v3; // rdi@1
signed int v5; // ebx@4
__int64 v6; // rax@6
__int64 v7; // rax@6
unsigned int v8; // edx@6
v2 = a2;
v3 = a1;
if ( !KiKvaShadow )
return 1i64;
if ( MmCreateShadowMapping(a2, 20480i64) )
{
v5 = 0;
if ( MmCreateShadowMapping(v3 + 28288, 4096i64) )
{
v5 = 1;
if ( *(_DWORD *)(v3 + 36) )
return 1i64;
LODWORD(v6) = RtlImageNtHeader(0x140000000i64);
LODWORD(v7) = RtlSectionTableFromVirtualAddress(
v6,
0x140000000i64,
(unsigned int)KiDivideErrorFaultShadow - 0x40000000);
v8 = *(_DWORD *)(v7 + 16);
if ( *(_DWORD *)(v7 + 8) > v8 )
v8 = *(_DWORD *)(v7 + 8);
if ( MmCreateShadowMapping(*(_DWORD *)(v7 + 12) + 0x140000000i64, (v8 + 4095) & 0xFFFFF000) )
return 1i64;
}
MmDeleteShadowMapping(v2, 20480i64);
if ( v5 )
MmDeleteShadowMapping(v3 + 28288, 4096i64);
}
return 0i64;
}
似乎是把 KiDivideErrorFaultShadow 所在的section这几块内核内存单独拿出来创建了映射。
刚好这一块都是idt和 syscall/sysenter 的入口,在KVASCODE 这个section里
当然IDT要用shadow的那份
KiInitializeIdt(v5, TRUE);
第二个参数应该就是是否使用shadow idt
然后根据cpu是否有flushpcid功能(KiFlushPcid)选择shadow的方式 KiKvaShadowMode = 1 or 2?
然后看一下 sysenter 的入口,内核页表应该是被放在了 gs:7000h 里面,太简单了不做分析,jmp后的流程和以前老 Systemcall 是一样的
随便挑了个中断分析了下
然后是 AttachProcess 的时候也加了一些特技(以前就一句writecr3)
void __fastcall KiLoadDirectoryTableBase(PEPROCESS ProcessObj, unsigned __int64 DirTableBase)
{
unsigned __int64 NewCr3; // rbx@1
unsigned __int64 v3; // rax@2
bool v4; // zf@2
bool v5; // sf@2
unsigned __int64 v6; // rcx@10
unsigned __int64 v7; // rax@11
NewCr3 = DirTableBase;
if ( KiKvaShadow )
{
v3 = DirTableBase;
v4 = (DirTableBase & 2) == 0;
v5 = (DirTableBase & 2 & 0x80u) != 0i64;
if ( DirTableBase & 2 )
{
v3 = DirTableBase | 0x8000000000000000ui64;
v4 = (DirTableBase | 0x8000000000000000ui64) == 0;
v5 = ((DirTableBase | 0x8000000000000000ui64) & 0x8000000000000000ui64) != 0i64;
}
*MK_FP(__GS__, 28672i64) = v3;
KiSetAddressPolicy(0, v4, v5, 0, ProcessObj->Pcb.AddressPolicy);
}
if ( HvlEnlightenments & 1 )
HvlSwitchVirtualAddressSpace(NewCr3);
else
__writecr3(NewCr3);
if ( KiKvaShadow && !KiFlushPcid )
{
v6 = __readcr4();
if ( v6 & 0x20080 )
{
v7 = v6;
_bittestandcomplement(&v7, 7u);
__writecr4(v7);
49 30703 49 15231 0 0 3733 0 0:00:08 0:00:04 0:00:04 3734__writecr4(v6);
}
else
{
__writecr3(__readcr3());
}
}
}
页表还是 EPROCESS 里那个页表,只不过看起来是把 KvaShadow 暂时禁用了(不然一切换cr3 内核地址空间全没了 直接炸穿)
Detach 的时候也是直接调用的 KiLoadDriectoryTableBase,被内联了
暂时就先看这么多,如果还有新的欢迎补充
原文链接:https://bbs.pediy.com/thread-223805.htm
更多详情可参考看雪论坛专题帖:
详情戳左下角“阅读原文”
热门阅读
点击阅读原文/read,
更多干货等着你~